package argtrust.certprop;

import argtrust.measure.Measure;

/**
 * @author Chung-Wei Hang
 */
public class CertPropMeasure extends Measure {

    private double r, s, c;

    public CertPropMeasure() {
        super();
        r = s = 0;
        c = -1;
    }

    public CertPropMeasure(double r, double s) {
        super();
        this.r = r;
        this.s = s;
        c = -1;
    }

    public CertPropMeasure(CertPropMeasure t) {
        super();
        this.r = t.r;
        this.s = t.s;
        this.c = t.c;
    }

    // calculate probability
    public double p() {
        if ((r + s) == 0) {
            return 0;
        }
        return r / (r + s);
    }

    public void set(double r, double s) {
        this.r = r;
        this.s = s;
        this.c = -1;
    }

    public double r() {
        return r;
    }

    public double s() {
        return s;
    }

    public double c() {
        if (c == -1) {
            c = certainty();
        }
        return c;
    }

    public double b() {
        return c() * p();
    }

    public double d() {
        return c() * (1 - p());
    }

    public double u() {
        return 1 - c();
    }


    public double certainty() {
        return certaintyIntegral(r, s, 10) / 2;
    }

    private double certaintyIntegral(double r, double s, int steps) {
        double sum = 0;
        double p = p();
        double q = certaintyBinomial(r, s, steps) / Math.pow(p, r) / Math.pow(1 - p, s);
        double a1 = 0;
        double a2 = 0;
        double a3 = 0;
        double a4 = 0;
        double b0 = 0;
        double b1 = 0;
        double b2 = 0;
        double b3 = 0;
        int i = steps;
        double a = 0;
        double b = 1;

        while (i > 0) {
            a4 = Math.pow(p, (double) i);
            double h = (a4 - a) / 4;
            a1 = a + h;
            a2 = a + 2 * h;
            a3 = a4 - h;
            /*
            double f0 = Math.abs(Math.pow(Math.pow(a/p, r/s) * (1-a)/(1-p), s) - q);
            double f1 = Math.abs(Math.pow(Math.pow(a1/p, r/s) * (1-a1)/(1-p), s) - q);
            double f2 = Math.abs(Math.pow(Math.pow(a2/p, r/s) * (1-a2)/(1-p), s) - q);
            double f3 = Math.abs(Math.pow(Math.pow(a3/p, r/s) * (1-a3)/(1-p), s) - q);
            double f4 = Math.abs(Math.pow(Math.pow(a4/p, r/s) * (1-a4)/(1-p), s) - q);
             */
            double f0 = Math.abs(Math.pow(a / p, r) * Math.pow((1 - a) / (1 - p), s) - q);
            double f1 = Math.abs(Math.pow(a1 / p, r) * Math.pow((1 - a1) / (1 - p), s) - q);
            double f2 = Math.abs(Math.pow(a2 / p, r) * Math.pow((1 - a2) / (1 - p), s) - q);
            double f3 = Math.abs(Math.pow(a3 / p, r) * Math.pow((1 - a3) / (1 - p), s) - q);
            double f4 = Math.abs(Math.pow(a4 / p, r) * Math.pow((1 - a4) / (1 - p), s) - q);

            sum += 2 * h * (7 * f0 + 32 * f1 + 12 * f2 + 32 * f3 + 7 * f4) / 45;
            b0 = 1 - Math.pow(1 - p, (double) i);
            h = (b - b0) / 4;
            b1 = b0 + h;
            b2 = b0 + 2 * h;
            b3 = b - h;
            /*
            f0 = Math.abs(Math.pow(Math.pow(b0/p, r/s) * (1-b0)/(1-p), s) - q);
            f1 = Math.abs(Math.pow(Math.pow(b1/p, r/s) * (1-b1)/(1-p), s) - q);
            f2 = Math.abs(Math.pow(Math.pow(b2/p, r/s) * (1-b2)/(1-p), s) - q);
            f3 = Math.abs(Math.pow(Math.pow(b3/p, r/s) * (1-b3)/(1-p), s) - q);
            f4 = Math.abs(Math.pow(Math.pow(b/p, r/s) * (1-b)/(1-p), s) - q);
             */
            f0 = Math.abs(Math.pow(b0 / p, r) * Math.pow((1 - b0) / (1 - p), s) - q);
            f1 = Math.abs(Math.pow(b1 / p, r) * Math.pow((1 - b1) / (1 - p), s) - q);
            f2 = Math.abs(Math.pow(b2 / p, r) * Math.pow((1 - b2) / (1 - p), s) - q);
            f3 = Math.abs(Math.pow(b3 / p, r) * Math.pow((1 - b3) / (1 - p), s) - q);
            f4 = Math.abs(Math.pow(b / p, r) * Math.pow((1 - b) / (1 - p), s) - q);

            sum += 2 * h * (7 * f0 + 32 * f1 + 12 * f2 + 32 * f3 + 7 * f4) / 45;
            a = a4;
            b = b0;
            i--;

        }

        return Math.abs(sum / q);
    }

    private double certaintyBinomial(double r, double s, int steps) {
        double sum = 0;
        double p = p();
        double a1 = 0;
        double a2 = 0;
        double a3 = 0;
        double a4 = 0;
        double b0 = 0;
        double b1 = 0;
        double b2 = 0;
        double b3 = 0;
        int i = steps;
        double a = 0;
        double b = 1;

        while (i > 0) {
            a4 = Math.pow(p, (double) i);
            double h = (a4 - a) / 4;
            a1 = a + h;
            a2 = a + 2 * h;
            a3 = a4 - h;
            /*
            double f0 = Math.pow(Math.pow(a/p, r/s) * (1-a)/(1-p), s);
            double f1 = Math.pow(Math.pow(a1/p, r/s) * (1-a1)/(1-p), s);
            double f2 = Math.pow(Math.pow(a2/p, r/s) * (1-a2)/(1-p), s);
            double f3 = Math.pow(Math.pow(a3/p, r/s) * (1-a3)/(1-p), s);
            double f4 = Math.pow(Math.pow(a4/p, r/s) * (1-a4)/(1-p), s);
             */
            double f0 = Math.pow(a / p, r) * Math.pow((1 - a) / (1 - p), s);
            double f1 = Math.pow(a1 / p, r) * Math.pow((1 - a1) / (1 - p), s);
            double f2 = Math.pow(a2 / p, r) * Math.pow((1 - a2) / (1 - p), s);
            double f3 = Math.pow(a3 / p, r) * Math.pow((1 - a3) / (1 - p), s);
            double f4 = Math.pow(a4 / p, r) * Math.pow((1 - a4) / (1 - p), s);

            sum += 2 * h * (7 * f0 + 32 * f1 + 12 * f2 + 32 * f3 + 7 * f4) / 45;
            b0 = 1 - Math.pow(1 - p, (double) i);
            h = (b - b0) / 4;
            b1 = b0 + h;
            b2 = b0 + 2 * h;
            b3 = b - h;
            /*
            f0 = Math.pow(Math.pow(b0/p, r/s) * (1-b0)/(1-p), s);
            f1 = Math.pow(Math.pow(b1/p, r/s) * (1-b1)/(1-p), s);
            f2 = Math.pow(Math.pow(b2/p, r/s) * (1-b2)/(1-p), s);
            f3 = Math.pow(Math.pow(b3/p, r/s) * (1-b3)/(1-p), s);
            f4 = Math.pow(Math.pow(b/p, r/s) * (1-b)/(1-p), s);
             */
            f0 = Math.pow(b0 / p, r) * Math.pow((1 - b0) / (1 - p), s);
            f1 = Math.pow(b1 / p, r) * Math.pow((1 - b1) / (1 - p), s);
            f2 = Math.pow(b2 / p, r) * Math.pow((1 - b2) / (1 - p), s);
            f3 = Math.pow(b3 / p, r) * Math.pow((1 - b3) / (1 - p), s);
            f4 = Math.pow(b / p, r) * Math.pow((1 - b) / (1 - p), s);

            sum += 2 * h * (7 * f0 + 32 * f1 + 12 * f2 + 32 * f3 + 7 * f4) / 45;
            a = a4;
            b = b0;
            i--;

        }

        return sum * Math.pow(p, r) * Math.pow(1 - p, s);
    }
    /*
    @Override
    public CertPropMeasure clone()
    {
    return new CertPropMeasure(r, s);
    }
     */

    @Override
    public String toString() {
//        return String.format("(%.2f,%.2f,%.2f)[%.2f,%.2f]",
//                b(),
//                d(),
//                u(),
//                r,
//                s);
        return String.format("(%.2f,%.2f,%.2f)",
                b(),
                d(),
                u());

//        return String.format("%.2f[%.2f,%.2f]",
//                b(),
//                r,
//                s);
//        return String.valueOf("<" + r + "," + s + ">" + ":" + this.b() );
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        CertPropMeasure rhs = (CertPropMeasure) obj;
        return (b() == rhs.b()
                && d() == rhs.d()
                && u() == rhs.u());
    }
    public static void main(String[] args) {
        CertPropMeasureOperators o = new CertPropMeasureOperators();
        CertPropMeasure t1 = new CertPropMeasure(10d, 5d);
        CertPropMeasure t2 = new CertPropMeasure(100d, 10d);
        CertPropMeasure t3 = new CertPropMeasure(10d, 2d);

        System.out.println(o.eq(t1, t2)); // false;
        System.out.println(t1.p()); // 0.6666666666666666;
        System.out.println(t1.c()); // 0.5229202114793706;
        System.out.println(t1.b()); // 0.3486134743195804;
        System.out.println(t3.b()); // 0.4764190013004013;
        System.out.println(o.lt(t3, t1)); // false;
        System.out.println(o.gt(t3, t1)); // true;
        t3.set(10d, 5d);
        System.out.println(o.eq(t3, t1)); // true;
        System.out.println(o.concatenate(t1, t2)); // <34.86134743195804,3.486134743195804>;
        System.out.println(o.aggregate(t1, t2)); // <110.0,15.0>;
    }
}
